package com.lianxi.dao;

import com.lianxi.es.Page;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;

//@Transactional("transactionManager")
public class BaseDao<T> implements IBaseDao<T> {

//    @Autowired
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }


//    @Inject
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    /**
     * 创建一个Class的对象来获取泛型的class
     */
    private Class<?> clz;

    private Class<?> getClz() {
        if (clz == null) {
            // 获取泛型的Class对象
            clz = ((Class<?>) (((ParameterizedType) (this.getClass()
                    .getGenericSuperclass())).getActualTypeArguments()[0]));
        }
        return clz;
    }


    @Override
    public int add(T t) {
        int id = 0;
        try {
            id = (int) this.getSession().save(t);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return id;
    }

    @Override
    public String addUUID(T t) {
        String id = "";
        try {
            id = (String) this.getSession().save(t);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return id;
    }

    @Override
    public void update(T t) {
        this.getSession().merge(t);
    }

    @Override
    public T get(int id) {
        return (T) this.getSession().get(getClz(), id);
    }

    @Override
    public T getDetached(int id) {
        Session session = this.getSession();
        T t = (T) session.get(getClz(), id);
        //将对象实例从session缓存清除,由持久态转为脱管态
        session.evict(t);
        return t;
    }

    @Override
    public T load(int id) {
        return (T) this.getSession().load(getClz(), id);
    }

    @Override
    public void delete(int id) {
        this.getSession().delete(this.load(id));
    }

    @Override
    public Pager defaultPagerList(String hql, Object[] params, Pager pager) {
        hql = hqlToHibernate5(hql);
        Integer page = 1;
        Integer length = 15;
        Integer start = (page - 1) * length;
        //获得总行数
        String countHql = "SELECT count(id) " + hql.substring(hql.indexOf("from"), hql.length());
        Long count = this.getCount(countHql, params);
        if (pager != null) {
            if (pager.getPage() != 0) {
                page = pager.getPage();
                start = (page - 1) * length;
            }
            if (pager.getLength() != 0) {
                length = pager.getLength();
            }
            List<T> list = findByPage(hql, params, start, length);
            pager.setData(list);
            pager.setCount(count.intValue());
            pager.setPages((count.intValue() + length - 1) / length);
            return pager;
        } else {
            pager = new Pager(page, length);
        }
        List<T> list = findByPage(hql, params, start, length);
        pager.setData(list);
        pager.setCount(count.intValue());
        pager.setPages((count.intValue() + length - 1) / length);
        return pager;
    }

    @Override
    public Pager findByPager(String hql, Object[] params, Pager pager) {
        hql = hqlToHibernate5(hql);
        if (pager != null) {
            if (pager.getPage() == 0) {
                return defaultPagerList(hql, params, pager);
            }
            if (pager.getLength() == 0) {
                return defaultPagerList(hql, params, pager);
            }
            int page = pager.getPage();
            int length = pager.getLength();
            List<T> list = findByPage(hql, params, (page - 1) * length, length);
            pager.setData(list);
            //获得总行数
            hql = getCountHql(hql, true);
            Long count = this.getCount(hql, params);
            pager.setCount(count.intValue());
            pager.setPages((count.intValue() + length - 1) / length);
            return pager;
        }
        return defaultPagerList(hql, params, pager);
    }

    public Page<T> findByPager(String hql, Object[] params, Page page) {
        int pageNumber = page.getPageNumber();
        int length = page.getPageSize();
        List<T> list = listByPage(hql, params, (pageNumber - 1) * length, length);
        page.setDatas(list);
        //获得总行数
        hql = getCountHql(hql, true);
        Long count = this.getCount(hql, params);
        page.setTotalRow(count.intValue());
        page.setTotalPage((count.intValue() + length - 1) / length);
        return page;

    }

    public List<T> listByPage(String hql, Object[] args, int start, int pageSize) {
        hql = hqlToHibernate5(hql);
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        query.setFirstResult(start).setMaxResults(pageSize);
        return query.list();
    }

    @Override
    public Pager findByPagerManyIn(String hql, Object[] params, Pager pager) {
        hql = hqlToHibernate5(hql);
        if (pager != null) {
            if (pager.getPage() == 0) {
                return defaultPagerList(hql, params, pager);
            }
            if (pager.getLength() == 0) {
                return defaultPagerList(hql, params, pager);
            }
            int page = pager.getPage();
            int length = pager.getLength();
            List<T> list = findByPageManyIn(hql, params, (page - 1) * length, length);
            pager.setData(list);
            //获得总行数
//            hql = getCountHql(hql, true);
//            Long count = this.getCount(hql, params);
//            pager.setCount(count.intValue());
//            pager.setPages((count.intValue() + length - 1) / length);
            return pager;
        }
        return defaultPagerList(hql, params, pager);
    }

    public List<T> list(String hql, Object[] args) {
        hql = hqlToHibernate5(hql);
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        return query.list();
    }

    public List<T> list(String hql, Object param) {
        hql = hqlToHibernate5(hql);
        return this.list(hql, new Object[]{param});
    }

    public List<T> list(String hql) {
        hql = hqlToHibernate5(hql);
        return this.list(hql, null);
    }

    public List<T> list(String hql, Object[] args, int first, int end) {
        hql = hqlToHibernate5(hql);
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        return query.setFirstResult(first).setMaxResults(end).list();
    }

    public Object loadEntity(int id, Class clz) {
        return getSession().load(clz, id);
    }

    public List<?> listObj(String hql, Object[] args) {
        hql = hqlToHibernate5(hql);
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        return query.list();
    }

    public List<?> listObj(String hql, Object obj) {
        hql = hqlToHibernate5(hql);
        return this.listObj(hql, new Object[]{obj});
    }

    public List<?> listObj(String hql) {
        hql = hqlToHibernate5(hql);
        return this.listObj(hql, null);
    }


    public List<T> list(String hql, Object obj, int first, int end) {
        hql = hqlToHibernate5(hql);
        return this.list(hql, new Object[]{obj}, first, end);
    }

    public List<T> list(String hql, int first, int end) {
        hql = hqlToHibernate5(hql);
        return this.list(hql, null, first, end);
    }

    public Object queryObject(String hql, Object[] args) {
        hql = hqlToHibernate5(hql);
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        return query.uniqueResult();
    }

    public Object queryObject(String hql, Object param) {
        return this.queryObject(hql, new Object[]{param});
    }

    public Object queryObject(String hql) {
        return this.queryObject(hql, null);
    }

    /**
     * 更新数据
     *
     * @param hql
     * @param args
     */
    public void updateByHql(String hql, Object[] args) {
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        query.executeUpdate();
    }

    public void updateByHql(String hql, Object param) {
        hql = hqlToHibernate5(hql);
        this.updateByHql(hql, new Object[]{param});
    }

    public void updateByHql(String hql) {
        hql = hqlToHibernate5(hql);
        this.updateByHql(hql, null);
    }

    /**
     * sql查询
     *
     * @param sql
     * @param args
     * @param clz
     * @param hasEntity 是否是实体
     * @return
     */
    @SuppressWarnings("unchecked")
    public <N extends Object> List<N> listBySql(String sql, Object[] args,
                                                Class<?> clz, boolean hasEntity) {

        SQLQuery sq = this.getSession().createSQLQuery(sql);
        setParameter(sq, args);
        if (hasEntity) {
            sq.addEntity(clz);
        } else {
            sq.setResultTransformer(Transformers.aliasToBean(clz));
        }
        return sq.list();
    }

    public <N extends Object> List<N> listBySqlAndList(String sql, List<Object> list,
                                                       Class<?> clz, boolean hasEntity) {

        SQLQuery sq = this.getSession().createSQLQuery(sql);
        setParameterQuery(sq, list);
        if (hasEntity) {
            sq.addEntity(clz);
        } else {
            sq.setResultTransformer(Transformers.aliasToBean(clz));
        }
        return sq.list();
    }

    public <N extends Object> List<N> listBySql(String sql, Object param,
                                                Class<?> clz, boolean hasEntity) {
        return this.listBySql(sql, new Object[]{param}, clz, hasEntity);
    }

    public <N extends Object> List<N> listBySql(String sql, Class<?> clz,
                                                boolean hasEntity) {
        return this.listBySql(sql, null, clz, hasEntity);
    }

    public Long getCount(String hql, Object[] objs) {
        hql = hqlToHibernate5(hql);
        Query query = this.getSession().createQuery(hql);
        setParameter(query, objs);
        Object o = query.uniqueResult();
        if (o == null){
            return 0L;
        }
        return (Long) o;
    }

    public Long getCount(String hql, Object obj) {
        hql = hqlToHibernate5(hql);
        return this.getCount(hql, new Object[]{obj});
    }

    public Long getCount(String hql) {
        hql = hqlToHibernate5(hql);
        return this.getCount(hql, null);
    }


    public List<T> listLimit(String hql, Object[] args, int page, int row) {
        hql = hqlToHibernate5(hql);
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        query.setFirstResult((page - 1) * row).setMaxResults(row);
        return query.list();
    }

    public List<T> listLimit(String hql, Object obj, int page, int row) {
        hql = hqlToHibernate5(hql);
        return this.listLimit(hql, new Object[]{obj}, page, row);
    }

    public List<T> listLimit(String hql, int page, int row) {
        hql = hqlToHibernate5(hql);
        return this.listLimit(hql, null, page, row);
    }

    /**
     * 直接计算，不用按页数来分了
     *
     * @param hql
     * @param args
     * @param start
     * @param pageSize
     * @return
     */
    public List<T> findByPage(String hql, Object[] args, int start, int pageSize) {
        hql = hqlToHibernate5(hql);
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        query.setFirstResult(start).setMaxResults(pageSize);
        return query.list();
    }

    public List<T> findByPageManyIn(String hql, Object[] args, int start, int pageSize) {
        hql = hqlToHibernate5(hql);
        Query query = this.getSession().createQuery(hql);
        setParameterManyIn(query, args);
        query.setFirstResult(start).setMaxResults(pageSize);
        return query.list();
    }

    public List<T> findByPage(String hql, Object obj, int page, int pageSize) {
        hql = hqlToHibernate5(hql);
        return this.findByPage(hql, new Object[]{obj}, page, pageSize);
    }

    public List<T> findByPage(String hql, int page, int pageSize) {
        hql = hqlToHibernate5(hql);
        return this.findByPage(hql, null, page, pageSize);
    }

    public void executeSQL(String sql, Object[] params) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        setParameter(query, params);
        query.executeUpdate();
        this.getSession().flush();
        this.getSession().clear();
    }

    public Long countBySQL(String sql, Object[] params) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        setParameter(query, params);
        BigInteger count = (BigInteger) query.uniqueResult();
        this.getSession().flush();
        this.getSession().clear();
        if (count != null) {
            return count.longValue();
        } else {
            return 0L;
        }
    }

    public Long sumBySQL(String sql, Object[] params) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        setParameter(query, params);
        Object object = query.uniqueResult();
        if (object == null) {
            return 0L;
        }
        BigDecimal decimal = new BigDecimal(object.toString());
        Long count = decimal.longValue();
        this.getSession().flush();
        this.getSession().clear();
        if (count != null) {
            return count.longValue();
        } else {
            return 0L;
        }
    }

    public Long countBySQL(String sql, List<Object> list) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        setParameterQuery(query, list);
        BigInteger count = (BigInteger) query.uniqueResult();
        this.getSession().flush();
        this.getSession().clear();
        if (count != null) {
            return count.longValue();
        } else {
            return 0L;
        }
    }

    /**
     * 根据顺序来设置参数
     *
     * @param query
     * @param args
     */
    private void setParameter(Query query, Object[] args) {
        if (args != null && args.length > 0) {
            int index = 0;
            for (Object arg : args) {
                if (arg instanceof Collection) {
                    query.setParameterList("depids", (Collection) arg);
                } else {
                    query.setParameter(index++, arg);
                }
            }
        }
    }

    /**
     * 根据顺序来设置参数
     *
     * @param query
     * @param args
     */
    private void setParameterManyIn(Query query, Object[] args) {
        if (args != null && args.length > 0) {
            int index = 0;
            for (Object arg : args) {
                if (arg instanceof Collection) {
                    query.setParameterList("depids"+index, (Collection) arg);
                    index++;
                } else {
                    query.setParameter(index++, arg);
                }
            }
        }
    }

    private void setParameterQuery(Query query, List<Object> list) {
        if (null != list) {
            int len = list.size();
            if (len > 0) {
                for (int i = 0; i < len; i++) {
                    if (list.get(i) instanceof Collection) {
                        query.setParameterList("depids", (Collection) list.get(i));
                    } else {
                        query.setParameter(i, list.get(i));
                    }
                }
            }
        }
    }

    private Query setParameterQuery(String hql, Object[] objs) {
        Query q = this.getSessionFactory().getCurrentSession().createQuery(hql);
        if (objs != null) {
            for (int i = 0; i < objs.length; i++) {
                q.setParameter(i, objs);
            }
        }
        return q;
    }

    private String getCountHql(String hql, boolean isHql) {
        String e = hql.substring(hql.indexOf("from"));
        String c = "select count(*) " + e;
        if (isHql) {
            c.replaceAll("fetch", "");
        }
        return c;
    }

    private String hqlToHibernate5(String hql) {
        String[] tmp = hql.split(" ");
        String hqlTmp = "";
        for (int i = 0, j = 0; i < tmp.length; i++) {
            String t = tmp[i].trim();

            if (t.indexOf("?") > -1) {
                hqlTmp = hqlTmp.concat(" ?").concat(String.valueOf(j)).concat(" ");
                j++;
            } else {
                hqlTmp = hqlTmp.concat(t).concat(" ");
            }
        }
        return hqlTmp;
    }
}
